%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: Peter
%% Progress to usefulness:
%%
%% Implementation of a fully meshed network.
%% Provides a lookup function from key to node.
%% 
%% External function calls:
%% 
%% Join the network 'Node' belongs to
%% {join, Node} -> {ok, Succ}
%% {leave} -> Succ    :: atom -- Leave current network and return successor
%% {lookup, Id} -> ResponsibleNode  :: atom --Find the node responsible for 'Id'
%% {successors} -> successors  :: [atom]
%%%%%%%%%%%%%%%%%%%%%%%

-module(mesh).
-behaviour(gen_server).
-include("db.hrl").

-export([start_link/0,start_link/1]).
-export([init/1, handle_call/3,handle_cast/2,handle_info/2
        ,code_change/3,terminate/2]).


start_link() ->
    io:format("Loading Mesh Network Module\n"),
    gen_server:start_link({local,?NETWORK},?MODULE,[],[]).

start_link(Args) ->
    io:format("Loading Mesh Network Module\n"),
    gen_server:start_link({local,?NETWORK},?MODULE,Args,[]).
%
% Nodes are stored in an ordered set as tuples
% containing {hashed nodename, nodename}
%
init(State) ->
    {ok, new_state(State)}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   External "function calls"
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% This hook is called by the node module to tell a node
% that it is the first node in the circle. 
% It is not used in this implementation. 
handle_call({start_ring}, _From, State) ->
    {reply, {ok}, State};

% This node want to join a chord network, knows about 'Node' and 
% ask for list with connected nodes and values to be responsible for
%
handle_call({join, Node}, _From, _State) ->
    {State,Succ} = gen_server:call({?NETWORK, Node} ,{joinRequest, node()}),
    [erlang:monitor(process, {?NETWORK,N}) || {_,N} <- State#mesh.nodes],
    NewState = succ_update(State),
    print_nodes(NewState),
    {reply, {ok, Succ}, NewState};

%
% This node wants to leave the network
%
handle_call({leave}, _From, State) ->
    % Send info about leaving
    cast_nodes({leaving,node()},State),
    NewState = del_node(node(),State),
    Dest = find_responsible_node(node(), NewState),
    {reply, Dest, new_state(State)};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 
% The lookup call should be done in parallel.
% 
% Is this needed for this implementation? 
%
handle_call({lookup, Id}, From, State) ->
    % Start a worker process, include state for nodelist
    {ok,Pid} = mesh_worker_sup:start_worker(State),
    gen_server:cast(Pid,{lookup, Id, From}),
    {noreply, State};

handle_call({successors}, _From, State) ->
    Succs = State#mesh.successors,
    {reply, Succs, State};

% Return status overview
handle_call({status}, _From, State) ->
    Fmt =
"#nodes: ~p
#successors setting: ~p
#successors currently: ~p
verbose: ~p
predecessor: ~p
",
    Params = [length(State#mesh.nodes),
              State#mesh.numberOfSuccs, length(State#mesh.successors),
              State#mesh.verbose, State#mesh.predecessor],
    {reply, {Fmt, Params}, State};

% Returns the whole state
handle_call({status_full}, _From, State) ->
    Fmt =
"#nodes: ~p
#successors setting: ~p
#successors currently: ~p
verbose: ~p
predecessor: ~p
successors: ~p
nodes: ~p
",
    Params = [length(State#mesh.nodes),
              State#mesh.numberOfSuccs, length(State#mesh.successors),
              State#mesh.verbose, State#mesh.predecessor,
              State#mesh.successors, State#mesh.nodes],
    {reply, {Fmt, Params}, State};

% Returns the list of nodes
handle_call({status_nodes}, _From, State) ->
    Nodes = [ Node || {_, Node} <- State#mesh.nodes ],
    {reply, Nodes, State};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   Internal "function calls"
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%
% Received a join request, some node wants to join this chord network
% Send {State,Succ} as reply
%
handle_call({joinRequest, Node}, _From, State) ->
    % Monitor process
    erlang:monitor(process,{?NETWORK,Node}),
    % Send info about the new node to everyone
    cast_nodes({newNode, Node}, State),
    NewState = add_node(Node, State),
    NewState2 = succ_update(NewState),
    Dest = find_responsible_node(Node, State),
    print_nodes(NewState2),
    {reply, {NewState2,Dest}, NewState2}.


%
% Received info about a new member of the chord network
%
handle_cast({newNode, Node}, State) ->
    % Monitor proess
    erlang:monitor(process,{?NETWORK,Node}),
    NewState = add_node(Node, State),
    NewState2 = succ_update(NewState),
    print_nodes(NewState2),
    {noreply, NewState2};


%
% Catch signal from leaving node
%
handle_cast({leaving, Node}, State) ->
    NewState = del_node(Node,State),
    NewState2 = succ_update(NewState),
    print_nodes(NewState2),
    {noreply, NewState2}.

%
% Function called when terminated
%
terminate(_Reason,_State) ->
    io:format("Terminating Mesh module~n").


handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) ->
    {_,Node} = Pid,
    NewState = del_node(Node,State),
    case (Node == State#mesh.predecessor) or
         (lists:member(Node, State#mesh.successors)) of
        true  ->
            NewState2 = succ_update(NewState),
            gen_server:cast(?BACKEND,{update_backup});
        false ->
            NewState2 = NewState
    end,
    print_nodes(NewState2),
    {noreply, NewState2};

handle_info(Info, State) ->
    io:format("Received info message: ~w~n", Info),
    {noreply ,State}.

code_change(_OldVsn, _State, _Extra) ->
    undefined.


% % % % % % % % % % % % % % % % % % % % % %
%    State modify functions               %
% % % % % % % % % % % % % % % % % % % % % % 

% Return a new state
new_state(State) ->
    NewState = State#mesh{nodes=ordsets:new()},
    NewState2 = add_node(node(), NewState),
    succ_init(NewState2).
    %NewState3 = succ_init(NewState2),
    %Clocks = dict:store(node(), 0, dict:new()),
    %NewState3#mesh{clocks=Clocks}.

% Update state with new node, returns new state
add_node(Node, State) ->
    Nodes = State#mesh.nodes,
    Nodes2 = ordsets:add_element({hash:hash(Node),Node},Nodes),
    State#mesh{nodes = Nodes2}.

% Delete a node from state object, returns new state
del_node(Node, State) ->
    Nodes = State#mesh.nodes,
    Nodes2 = ordsets:del_element({hash:hash(Node),Node},Nodes),
    State#mesh{nodes = Nodes2}.

% Send Msg to all nodes
cast_nodes(Msg,State) ->
    Nodes = State#mesh.nodes,
    [gen_server:cast({?NETWORK,X},Msg) || {_,X} <- Nodes, X /= node()].

% Print nodes
print_nodes(State) ->
    case State#mesh.verbose of
        true  ->
            Nodes = State#mesh.nodes,
            %Clocks = State#mesh.clocks,
            io:format("Nodes: ~w~n",[[X || {_,X} <- Nodes]]),
            io:format("Successors: ~w~n",[State#mesh.successors]),
            io:format("Predeccessor: ~w~n", [State#mesh.predecessor]);
            %io:format("VC: ~p~n", [dict:to_list(Clocks)]);
        false ->
            ok
    end.

% % % % % % % % % % % % % % % % % % % % % %
% Successorlist modifiers

succ_init(State) ->
    % Initialize successors to itself
    Succ_init = lists:duplicate(State#mesh.numberOfSuccs, node()),
    State#mesh{successors = Succ_init, predecessor = node()}.

% Update successor list
succ_update(State) ->
    Nodes = State#mesh.nodes,
    NoSuccs = State#mesh.numberOfSuccs,
    Nodes_repeated = lists:flatten(lists:duplicate(
                     erlang:round(NoSuccs / length(Nodes))+2, Nodes)),
    Nodes_rep2 = lists:nthtail(1,
                 lists:dropwhile(fun({_,Node}) -> Node /= node() end, 
                                 Nodes_repeated)),
    {New_list,_} = lists:split(NoSuccs, Nodes_rep2),
    New_list2 = [X || {_,X} <- New_list],
    Pred = element(2,lists:nth(2,lists:dropwhile(fun({_,Node}) -> 
                   Node /= node() end, lists:reverse(Nodes_repeated)))),
    State#mesh{successors=New_list2, predecessor = Pred}.


% % % % % % % % % % % % % % % % % % % % % %
% Vector clock modifiers

% Increment own clock
%inc_clock(State) ->
%    Clocks = State#mesh.clocks,
%    NewClocks = dict:update(node(), fun(X) -> X+1 end, Clocks),
%    State#mesh{clocks=NewClocks}.

% Update VC table
%update_vc(VC, State) ->
%    Clocks = State#mesh.clocks,
%    NewClocks = dict:merge(fun(K,V1,V2) -> max_clock(K,V1,V2) end,Clocks,VC),
%    State#mesh{clocks=NewClocks}.

% Remove a node from VC
%remove_clock(Node, State) ->
%    Clocks = State#mesh.clocks,
%    NewClocks = dict:erase(Node,Clocks),
%    State#mesh{clocks=NewClocks}.

% % % % % % % % % % % % % % % % % % % % % %
%    Helper functions                     %
% % % % % % % % % % % % % % % % % % % % % % 

%
% Given a value (Node or Key), find
% the responsible node

find_responsible_node(Value, State) ->
    Nodes = State#mesh.nodes,
    List = ordsets:to_list(Nodes),
    case length(List) == 0 of
        true  -> last_node;
        false -> List2 = lists:dropwhile(fun({Hash,_}) -> 
                            Hash < hash:hash(Value) end,List),
                 case length(List2) of
                    0 -> element(2,lists:nth(1,List));
                    _ -> element(2,lists:nth(1,List2))
                 end
    end.

% Set VC to the clock with highest value
%max_clock(K,V1,V2) ->
%    case K == node() of
%        true  -> V1;
%        false -> max(V1,V2)
%    end.
